# frozen_string_literal: true

require 'spec_helper'

RSpec.describe VulnerabilityExternalIssueLinks::CreateService, feature_category: :vulnerability_management do
  let(:user) { create(:user) }
  let(:project) { create(:project) }
  let(:pipeline) { create(:ci_pipeline, :success, project: project) }
  let(:finding) { create(:vulnerabilities_finding, :with_pipeline, project: project, severity: :high) }
  let(:vulnerability) { create(:vulnerability, title: 'My vulnerability', project: project, findings: [finding]) }
  let(:external_provider) { 'jira' }
  let(:service) { described_class.new(user, vulnerability, external_provider) }

  subject(:create_external_issue_link) { service.execute }

  context 'for jira' do
    let(:configured_to_create_issues_from_vulnerabilities) { false }
    let(:jira_integration) { double(configured_to_create_issues_from_vulnerabilities?: configured_to_create_issues_from_vulnerabilities, project_key: 'GV') }

    before do
      allow(project).to receive(:jira_integration).and_return(jira_integration)
      project.add_developer(user)
    end

    context 'when jira service is not configured to create issues' do
      it { is_expected.not_to be_success }

      it 'returns response with error messages' do
        expect(subject.message).to eq(['External provider service is not configured to create issues.'])
      end
    end

    context 'when jira service is configured to create issues' do
      let(:configured_to_create_issues_from_vulnerabilities) { true }

      context 'and there is already created external issue link for given vulnerability' do
        before do
          create(:vulnerabilities_external_issue_link, vulnerability: vulnerability)
        end

        context 'and the external issue exists' do
          let!(:jira_issue) { ExternalIssue.new('JIRA-123', project) }

          before do
            allow(jira_integration).to receive(:find_issue).and_return(jira_issue)
          end

          it { is_expected.not_to be_success }

          it 'returns response with error messages' do
            expect(subject.message).to match_array(['Vulnerability already has a "created" issue link'])
          end
        end

        context 'and the external issue does not exist' do
          let(:jira_issue_id) { nil }
          let(:errors) { {} }
          let(:jira_issue) { double(has_errors?: errors.present?, id: jira_issue_id, errors: errors) }

          before do
            allow(jira_integration).to receive(:create_issue).and_return(jira_issue)
            allow(jira_integration).to receive(:find_issue).and_return(nil)
          end

          it 'creates issue using jira service' do
            expect(jira_integration).to receive(:create_issue).with("Investigate vulnerability: #{vulnerability.title}", kind_of(String), user)

            subject
          end
        end
      end

      context 'and there is no external issue link created for given vulnerability' do
        let(:jira_issue_id) { nil }
        let(:errors) { {} }
        let(:jira_issue) { double(has_errors?: errors.present?, id: jira_issue_id, errors: errors) }

        before do
          allow(jira_integration).to receive(:create_issue).and_return(jira_issue)
        end

        it 'creates issue using jira service' do
          expect(jira_integration).to receive(:create_issue).with("Investigate vulnerability: #{vulnerability.title}", kind_of(String), user)

          subject
        end

        context 'and issue creation fails in Jira' do
          let(:errors) { { 'summary' => 'The issue summary is invalid.' } }

          it { is_expected.not_to be_success }

          it 'returns response with error messages' do
            expect(subject.message).to eq(['The issue summary is invalid.'])
          end
        end

        context 'and issue creation succeeds in Jira' do
          context 'and external issue link creation fails' do
            let(:jira_issue_id) { nil }

            it { is_expected.not_to be_success }

            it 'returns response with error messages' do
              expect(subject.message).to eq(['External issue key can\'t be blank'])
            end
          end

          context 'and external issue link creation succeeds' do
            let(:jira_issue_id) { '10001' }

            it { is_expected.to be_success }

            it 'creates external issue link in database' do
              expect { subject }.to change { ::Vulnerabilities::ExternalIssueLink.count }.by(1)
            end

            it 'responds with created external issue link in payload', :aggregate_failures do
              external_issue_link = subject.payload[:record]

              expect(external_issue_link).to be_a(::Vulnerabilities::ExternalIssueLink)
              expect(external_issue_link).to have_attributes(
                external_type: 'jira',
                external_project_key: 'GV',
                external_issue_key: '10001',
                link_type: 'created',
                author_id: user.id,
                vulnerability_id: vulnerability.id
              )
            end
          end
        end
      end
    end
  end
end
